/*
* NET.java Copyright (C) 2003
* 
* $Id: NET.java,v 1.10 2006/01/11 22:04:42 hzi Exp $
*/
/*
* Copyright (C) 1997-2001 Id Software, Inc.
* 
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
* 
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE.
* 
* See the GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307, USA.
*  
*/
using System;
using Defines = Suake2.UI.Defines;
using Globals = Suake2.UI.Globals;
using cvar_t = Suake2.UI.game.cvar_t;
using Com = Suake2.UI.qcommon.Com;
using Cvar = Suake2.UI.qcommon.Cvar;
using netadr_t = Suake2.UI.qcommon.netadr_t;
using sizebuf_t = Suake2.UI.qcommon.sizebuf_t;
using Lib = Suake2.UI.util.Lib;
//UPGRADE_TODO: The type 'java.net.InetSocketAddress' could not be found. If it was not included in the conversion, there may be compiler issues. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1262'"
using InetSocketAddress = java.net.InetSocketAddress;
//UPGRADE_TODO: The type 'java.net.SocketAddress' could not be found. If it was not included in the conversion, there may be compiler issues. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1262'"
using SocketAddress = java.net.SocketAddress;
//UPGRADE_TODO: The type 'java.nio.ByteBuffer' could not be found. If it was not included in the conversion, there may be compiler issues. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1262'"
using ByteBuffer = java.nio.ByteBuffer;
//UPGRADE_TODO: The type 'java.nio.channels.DatagramChannel' could not be found. If it was not included in the conversion, there may be compiler issues. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1262'"
using DatagramChannel = java.nio.channels.DatagramChannel;
namespace Suake2.UI.sys
{
	
	public sealed class NET
	{
		
		private const int MAX_LOOPBACK = 4;
		
		/// <summary>Local loopback adress. </summary>
		private static netadr_t net_local_adr = new netadr_t();
		
		public class loopmsg_t
		{
			public loopmsg_t()
			{
				InitBlock();
			}
			private void  InitBlock()
			{
				data = new sbyte[Defines.MAX_MSGLEN];
			}
			//UPGRADE_NOTE: The initialization of  'data' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
			internal sbyte[] data;
			
			internal int datalen;
		}
		
		
		public class loopback_t
		{
			public loopback_t()
			{
				msgs = new loopmsg_t[jake2.sys.NET.MAX_LOOPBACK];
				for (int n = 0; n < jake2.sys.NET.MAX_LOOPBACK; n++)
				{
					msgs[n] = new loopmsg_t();
				}
			}
			
			internal loopmsg_t[] msgs;
			
			internal int get_Renamed, send;
		}
		
		
		public static loopback_t[] loopbacks = new loopback_t[2];
		
		private static DatagramChannel[] ip_channels = new DatagramChannel[]{null, null};
		
		private static SupportClass.UdpClientSupport[] ip_sockets = new SupportClass.UdpClientSupport[]{null, null};
		
		/// <summary> Compares ip address and port.</summary>
		public static bool CompareAdr(netadr_t a, netadr_t b)
		{
			return (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3] && a.port == b.port);
		}
		
		/// <summary> Compares ip address without the port.</summary>
		public static bool CompareBaseAdr(netadr_t a, netadr_t b)
		{
			if (a.type != b.type)
				return false;
			
			if (a.type == Defines.NA_LOOPBACK)
				return true;
			
			if (a.type == Defines.NA_IP)
			{
				return (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3]);
			}
			return false;
		}
		
		/// <summary> Returns a string holding ip address and port like "ip0.ip1.ip2.ip3:port".</summary>
		public static System.String AdrToString(netadr_t a)
		{
			System.Text.StringBuilder sb = new System.Text.StringBuilder();
			sb.Append(a.ip[0] & 0xFF).Append('.').Append(a.ip[1] & 0xFF);
			sb.Append('.');
			sb.Append(a.ip[2] & 0xFF).Append('.').Append(a.ip[3] & 0xFF);
			sb.Append(':').Append(a.port);
			return sb.ToString();
		}
		
		/// <summary> Returns IP address without the port as string.</summary>
		public static System.String BaseAdrToString(netadr_t a)
		{
			System.Text.StringBuilder sb = new System.Text.StringBuilder();
			sb.Append(a.ip[0] & 0xFF).Append('.').Append(a.ip[1] & 0xFF);
			sb.Append('.');
			sb.Append(a.ip[2] & 0xFF).Append('.').Append(a.ip[3] & 0xFF);
			return sb.ToString();
		}
		
		/// <summary> Creates an netadr_t from an string.</summary>
		public static bool StringToAdr(System.String s, netadr_t a)
		{
			if (s.ToUpper().Equals("localhost".ToUpper()) || s.ToUpper().Equals("loopback".ToUpper()))
			{
				a.set_Renamed(net_local_adr);
				return true;
			}
			try
			{
				System.String[] address = s.split(":");
				//UPGRADE_TODO: The equivalent in .NET for method 'java.net.InetAddress.getByName' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'"
				System.Net.IPAddress ia = System.Net.Dns.Resolve(address[0]).AddressList[0];
				a.ip = SupportClass.ToSByteArray(ia.GetAddressBytes());
				a.type = Defines.NA_IP;
				if (address.Length == 2)
					a.port = Lib.atoi(address[1]);
				return true;
			}
			catch (System.Exception e)
			{
				//UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.getMessage' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'"
				Com.Println(e.Message);
				return false;
			}
		}
		
		/// <summary> Seems to return true, if the address is is on 127.0.0.1.</summary>
		public static bool IsLocalAddress(netadr_t adr)
		{
			return CompareAdr(adr, net_local_adr);
		}
		
		/*
		* ==================================================
		* 
		* LOOPBACK BUFFERS FOR LOCAL PLAYER
		* 
		* ==================================================
		*/
		
		/// <summary> Gets a packet from internal loopback.</summary>
		public static bool GetLoopPacket(int sock, netadr_t net_from, sizebuf_t net_message)
		{
			loopback_t loop;
			loop = loopbacks[sock];
			
			if (loop.send - loop.get_Renamed > MAX_LOOPBACK)
				loop.get_Renamed = loop.send - MAX_LOOPBACK;
			
			if (loop.get_Renamed >= loop.send)
				return false;
			
			int i = loop.get_Renamed & (MAX_LOOPBACK - 1);
			loop.get_Renamed++;
			
			Array.Copy(loop.msgs[i].data, 0, net_message.data, 0, loop.msgs[i].datalen);
			net_message.cursize = loop.msgs[i].datalen;
			
			net_from.set_Renamed(net_local_adr);
			return true;
		}
		
		/// <summary> Sends a packet via internal loopback.</summary>
		public static void  SendLoopPacket(int sock, int length, sbyte[] data, netadr_t to)
		{
			int i;
			loopback_t loop;
			
			loop = loopbacks[sock ^ 1];
			
			// modulo 4
			i = loop.send & (MAX_LOOPBACK - 1);
			loop.send++;
			
			Array.Copy(data, 0, loop.msgs[i].data, 0, length);
			loop.msgs[i].datalen = length;
		}
		
		/// <summary> Gets a packet from a network channel</summary>
		public static bool GetPacket(int sock, netadr_t net_from, sizebuf_t net_message)
		{
			
			if (GetLoopPacket(sock, net_from, net_message))
			{
				return true;
			}
			
			if (ip_sockets[sock] == null)
				return false;
			
			try
			{
				ByteBuffer receiveBuffer = ByteBuffer.wrap(net_message.data);
				
				InetSocketAddress srcSocket = (InetSocketAddress) ip_channels[sock].receive(receiveBuffer);
				if (srcSocket == null)
					return false;
				
				net_from.ip = srcSocket.getAddress().getAddress();
				net_from.port = srcSocket.getPort();
				net_from.type = Defines.NA_IP;
				
				int packetLength = receiveBuffer.position();
				
				if (packetLength > net_message.maxsize)
				{
					Com.Println("Oversize packet from " + AdrToString(net_from));
					return false;
				}
				
				// set the size
				net_message.cursize = packetLength;
				// set the sentinel
				net_message.data[packetLength] = 0;
				return true;
			}
			catch (System.IO.IOException e)
			{
				//UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.toString' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'"
				Com.DPrintf("NET_GetPacket: " + e + " from " + AdrToString(net_from) + "\n");
				return false;
			}
		}
		
		/// <summary> Sends a Packet.</summary>
		public static void  SendPacket(int sock, int length, sbyte[] data, netadr_t to)
		{
			if (to.type == Defines.NA_LOOPBACK)
			{
				SendLoopPacket(sock, length, data, to);
				return ;
			}
			
			if (ip_sockets[sock] == null)
				return ;
			
			if (to.type != Defines.NA_BROADCAST && to.type != Defines.NA_IP)
			{
				Com.Error(Defines.ERR_FATAL, "NET_SendPacket: bad address type");
				return ;
			}
			
			try
			{
				SocketAddress dstSocket = new InetSocketAddress(to.InetAddress, to.port);
				ip_channels[sock].send(ByteBuffer.wrap(data, 0, length), dstSocket);
			}
			catch (System.Exception e)
			{
				//UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.toString' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'"
				Com.Println("NET_SendPacket ERROR: " + e + " to " + AdrToString(to));
			}
		}
		
		/// <summary> OpenIP, creates the network sockets. </summary>
		public static void  OpenIP()
		{
			cvar_t port, ip, clientport;
			
			port = Cvar.Get("port", "" + Defines.PORT_SERVER, Defines.CVAR_NOSET);
			ip = Cvar.Get("ip", "localhost", Defines.CVAR_NOSET);
			clientport = Cvar.Get("clientport", "" + Defines.PORT_CLIENT, Defines.CVAR_NOSET);
			
			if (ip_sockets[Defines.NS_SERVER] == null)
			{
				//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
				ip_sockets[Defines.NS_SERVER] = Socket(Defines.NS_SERVER, ip.string_Renamed, (int) port.value_Renamed);
			}
			
			if (ip_sockets[Defines.NS_CLIENT] == null)
			{
				//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
				ip_sockets[Defines.NS_CLIENT] = Socket(Defines.NS_CLIENT, ip.string_Renamed, (int) clientport.value_Renamed);
			}
			if (ip_sockets[Defines.NS_CLIENT] == null)
				ip_sockets[Defines.NS_CLIENT] = Socket(Defines.NS_CLIENT, ip.string_Renamed, Defines.PORT_ANY);
		}
		
		/// <summary> Config multi or singlepalyer - A single player game will only use the loopback code.</summary>
		public static void  Config(bool multiplayer)
		{
			if (!multiplayer)
			{
				// shut down any existing sockets
				for (int i = 0; i < 2; i++)
				{
					if (ip_sockets[i] != null)
					{
						ip_sockets[i].Close();
						ip_sockets[i] = null;
					}
				}
			}
			else
			{
				// open sockets
				OpenIP();
			}
		}
		
		/// <summary> Init</summary>
		public static void  Init()
		{
			// nothing to do
		}
		
		/*
		* Socket
		*/
		public static SupportClass.UdpClientSupport Socket(int sock, System.String ip, int port)
		{
			
			SupportClass.UdpClientSupport newsocket = null;
			try
			{
				if (ip_channels[sock] == null || !ip_channels[sock].isOpen())
					ip_channels[sock] = DatagramChannel.open();
				
				if (ip == null || ip.Length == 0 || ip.Equals("localhost"))
				{
					if (port == Defines.PORT_ANY)
					{
						newsocket = ip_channels[sock].socket();
						newsocket.bind(new InetSocketAddress(0));
					}
					else
					{
						newsocket = ip_channels[sock].socket();
						newsocket.bind(new InetSocketAddress(port));
					}
				}
				else
				{
					//UPGRADE_TODO: The equivalent in .NET for method 'java.net.InetAddress.getByName' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'"
					System.Net.IPAddress ia = System.Net.Dns.Resolve(ip).AddressList[0];
					newsocket = ip_channels[sock].socket();
					newsocket.bind(new InetSocketAddress(ia, port));
				}
				
				// nonblocking channel
				ip_channels[sock].configureBlocking(false);
				// the socket have to be broadcastable
				newsocket.setBroadcast(true);
			}
			catch (System.Exception e)
			{
				//UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.toString' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'"
				Com.Println("Error: " + e.ToString());
				newsocket = null;
			}
			return newsocket;
		}
		
		/// <summary> Shutdown - closes the sockets </summary>
		public static void  Shutdown()
		{
			// close sockets
			Config(false);
		}
		
		/// <summary>Sleeps msec or until net socket is ready. </summary>
		public static void  Sleep(int msec)
		{
			if (ip_sockets[Defines.NS_SERVER] == null || (Globals.dedicated != null && Globals.dedicated.value_Renamed == 0))
				return ; // we're not a server, just run full speed
			
			try
			{
				//TODO: check for timeout
				//UPGRADE_TODO: Method 'java.lang.Thread.sleep' was converted to 'System.Threading.Thread.Sleep' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javalangThreadsleep_long'"
				System.Threading.Thread.Sleep(new System.TimeSpan((System.Int64) 10000 * msec));
			}
			catch (System.Threading.ThreadInterruptedException e)
			{
			}
			//ip_sockets[NS_SERVER].
			
			// this should wait up to 100ms until a packet
			/*
			* struct timeval timeout; 
			* fd_set fdset; 
			* extern cvar_t *dedicated;
			* extern qboolean stdin_active;
			* 
			* if (!ip_sockets[NS_SERVER] || (dedicated && !dedicated.value))
			* 		return; // we're not a server, just run full speed
			* 
			* FD_ZERO(&fdset);
			*  
			* if (stdin_active) 
			* 		FD_SET(0, &fdset); // stdin is processed too 
			* 
			* FD_SET(ip_sockets[NS_SERVER], &fdset); // network socket 
			* 
			* timeout.tv_sec = msec/1000; 
			* timeout.tv_usec = (msec%1000)*1000; 
			* 
			* select(ip_sockets[NS_SERVER]+1, &fdset, NULL, NULL, &timeout);
			*/
		}
		static NET()
		{
			{
				loopbacks[0] = new loopback_t();
				loopbacks[1] = new loopback_t();
			}
		}
	}
}